#include <qdebug.h>
#include <QtGui/QAction>
#include <QtGui/QStyleFactory>
#include <QtGui/QStyle>
#include <QtGui/QApplication>
#include <QtGui/QKeyEvent>
#include <QtCore/QPluginLoader>
#include <QtCore/QBuffer>
#include <QtCore/QSignalMapper>
#include <QtCore/QLibrary>
#include <QtDesigner/QDesignerCustomWidgetInterface>
#include <QtDesigner/abstractformwindowcursor.h>
#include <abstractformeditorplugin.h>

#include "actioneditorw.h"
#include "designerbridgew.h"
#include "eclipseintegration.h"
#include "formwindoww.h"
#include "objectinspectorw.h"
#include "propertyeditorw.h"
#include "resourceeditorw.h"
#include "signalsloteditorw.h"
#include "widgetboxw.h"
#include "private/pluginmanager_p.h"
#include "private/qdesigner_formbuilder_p.h"
#include "private/qdesigner_integration_p.h"

#include "formeditorw.h"

FormEditorW *FormEditorW::m_self = 0;


FormEditorW::FormEditorW(QObject *parent) : QObject(parent), m_formeditor(0), m_designerSignalSlotEditor(0), m_objectInspector(0), m_propertyEditor(0), m_widgetBox(0), m_actionEditor(0), m_signalSlotEditor(0), m_resourceEditor(0), m_initialized(false)
{
#if defined(QTJAMBI_ECLIPSE_INTEGRATION)
    QApplication::setLibraryPaths(QStringList());
#endif

    m_self = this;
    m_formeditor = QDesignerComponents::createFormEditor(parent);

    initPlugins();

    QDesignerComponents::initializeResources();
    QDesignerComponents::createTaskMenu(m_formeditor, this);
}

/**
 * 初始化。晚点初始化可以确保我们可以在不同的编辑器初始化之后装载插件
 */
void FormEditorW::initialize()
{
    if (!m_initialized)
    {
        m_initialized = true;

        m_objectInspector = ObjectInspectorW::instance();
        m_propertyEditor = PropertyEditorW::instance();
        m_widgetBox = WidgetBoxW::instance();
        m_actionEditor = ActionEditorW::instance();
        m_signalSlotEditor = SignalSlotEditorW::instance();
        m_resourceEditor = ResourceEditorW::instance();

        EclipseIntegration *integration = new EclipseIntegration(m_formeditor, this);
        connect(this, SIGNAL(updateCustomWidgetPlugins()), integration, SLOT(updateCustomWidgetPlugins()));
        m_formeditor->setIntegration(integration);
        connect(m_formeditor->formWindowManager(), SIGNAL(activeFormWindowChanged(QDesignerFormWindowInterface *)), this, SLOT(activeFormWindowChanged(QDesignerFormWindowInterface *)));

        setupPreviewActions();

        for(int id = 0; id <= lastAction(); ++id)
        {
            connect(idToAction(id), SIGNAL(changed()), new ActionChangedNotifier(this, id), SLOT(actionChanged()));
        }
    }
}

void FormEditorW::initPlugins()
{
    QDesignerComponents::initializePlugins(formEditor());
    initializeCorePlugins();
    Q_EMIT updateCustomWidgetPlugins();

    QDesignerPluginManager *pluginManager = m_formeditor->pluginManager();
    QStringList failedPlugins = pluginManager->failedPlugins();

    if (!failedPlugins.isEmpty())
        m_pluginFailureString = "";

    Q_FOREACH (QString plugin, failedPlugins)
        m_pluginFailureString += plugin + ": " + pluginManager->failureReason(plugin) + "\n";
}

QString FormEditorW::pluginFailureString() const
{
    return m_pluginFailureString;
}

void FormEditorW::setPluginPath(const QString &pluginPath)
{
    QDesignerPluginManager *pluginManager = m_formeditor->pluginManager();
    QStringList pluginPaths = pluginManager->pluginPaths();

    pluginPaths.append(pluginPath);
    pluginManager->setPluginPaths(pluginPaths);
    pluginManager->ensureInitialized();

    initPlugins();
}

bool FormEditorW::initializeJambiPlugins(const QString &pluginPath, const QString &jambiPluginPath, const QString &customWidgetClassPath, const QString &resourcePath, const QString &jvm)
{
    QDesignerPluginManager *pluginManager = m_formeditor->pluginManager();

    QStringList pluginPaths = pluginManager->pluginPaths();

    if (!m_jambiPluginPath.isEmpty())
        pluginPaths.removeAll(m_jambiPluginPath);

    {
        QStringList dllsToLoad;
        QStringList qtDlls;
        /*qtDlls << QString::fromLatin1("QtCore")
               << QString::fromLatin1("QtGui")
               << QString::fromLatin1("QtXml")
               << QString::fromLatin1("QtDesigner")
               << QString::fromLatin1("QtDesignerComponents");*/

#if defined(QT_DEBUG)
#  if defined(Q_OS_WIN)
        Q_FOREACH (QString dll, qtDlls)
            dllsToLoad << (dll + QString::fromLatin1("d4"));

        QString qtjambi_lib = QString::fromLatin1("qtjambi_debuglib");
        dllsToLoad << qtjambi_lib;
#  elif defined(Q_OS_LINUX)
        Q_FOREACH (QString dll, qtDlls)
            dllsToLoad << (QString::fromLatin1("lib") + dll);

        QString qtjambi_lib = QString::fromLatin1("libqtjambi_debuglib");
        dllsToLoad << qtjambi_lib;
#  endif
#else
#  if defined(Q_OS_WIN)
        Q_FOREACH (QString dll, qtDlls)
            dllsToLoad << (dll + QString::fromLatin1("4"));

        QString qtjambi_lib = QString::fromLatin1("qtjambi");
        dllsToLoad << qtjambi_lib;
#  elif defined(Q_OS_LINUX) // Q_WS_MAC
        Q_FOREACH (QString dll, qtDlls)
            dllsToLoad << (QString::fromLatin1("lib") + dll);

        QString qtjambi_lib = QString::fromLatin1("libqtjambi");
        dllsToLoad << qtjambi_lib;
#  endif
#endif


#if defined(Q_OS_LINUX)
        QString dllPath = QFileInfo(QString(pluginPath).replace('\\', '/')).absoluteFilePath() + QLatin1String("/lib/");
#else
        QString dllPath = QFileInfo(QString(pluginPath).replace('\\', '/')).absoluteFilePath() + QLatin1String("/bin/");
#endif

        Q_FOREACH (QString dll, dllsToLoad)
        {
            if (!QLibrary(dll).isLoaded())
            {
                QLibrary *lib = new QLibrary(dllPath + dll, this);
                lib->setLoadHints(QLibrary::ResolveAllSymbolsHint);
                lib->load();

                if (!lib->isLoaded())
                {
                    qWarning("QtJambi plugin failed to load required library '%s'", qPrintable(dllPath + dll));
                }
                else if (dll == qtjambi_lib)
                {
                    typedef void (*SetJVMHintFunction)(const QString&);

                    SetJVMHintFunction func = (SetJVMHintFunction)lib->resolve("qtjambi_set_vm_location_override");
                    Q_ASSERT_X(func, "FormEditorW::initializeJambiPlugins", "no set vm location override function.");
                    func(jvm);
                }
            }
        }
    }

    m_jambiPluginPath = jambiPluginPath;
    pluginPaths.append(m_jambiPluginPath);
    pluginManager->setPluginPaths(pluginPaths);

    QStringList registeredPlugins = pluginManager->registeredPlugins();
    int plugins_found = 0;

    Q_FOREACH (QString registeredPlugin, registeredPlugins)
    {
        QObject *plugin = pluginManager->instance(registeredPlugin);

        if (QDesignerFormEditorPluginInterface *formEditorPlugin = qobject_cast<QDesignerFormEditorPluginInterface*>(plugin))
        {
            plugins_found++;

            if (!formEditorPlugin->isInitialized())
                formEditorPlugin->initialize(formEditor());
        }
    }

    pluginManager->ensureInitialized();
    initPlugins();

    if (plugins_found >= 1)
    {
        plugins_found = 0;

		ResourceEditorW *resourceEditor = ResourceEditorW::instance();
		if (resourceEditor != 0)
		{
			resourceEditor->updateResources(resourcePath);
			plugins_found++;
		}

		WidgetBoxW *widgetBox = WidgetBoxW::instance();
		if (widgetBox != 0)
		{
			widgetBox->updateCustomWidgetLocation(customWidgetClassPath);
			plugins_found++;
		}
    }
    else
    {
        qWarning("Couldn't find all classes for Jambi plugins");
    }

    if (plugins_found < 2)
    {
        qWarning("Couldn't update resource browser and widget box");
        return false;
    }
    else
    {
        return true;
    }
}

void FormEditorW::setupPreviewActions()
{
    QSignalMapper *mapper = new QSignalMapper(this);
    connect(mapper, SIGNAL(mapped(const QString &)), this, SLOT(preview(const QString &)));

    QStringList styles = QStyleFactory::keys();
    styles.prepend("");

    for (int i = 0; i < styles.size(); ++i)
    {
        QAction *act = new QAction(styles.at(i).isEmpty() ? QLatin1String("Preview") : styles.at(i), this);
        act->setToolTip(styles.at(i).isEmpty() ? QLatin1String("Preview") : QLatin1String("Preview in ") + styles.at(i));
        mapper->setMapping(act, styles.at(i));
        connect(act, SIGNAL(triggered()), mapper, SLOT(map()));
        m_previewActions.append(act);
    }
}

void FormEditorW::preview(const QString &style_name)
{
    if (QDesignerFormWindowInterface *fw = m_formeditor->formWindowManager()->activeFormWindow())
    {
        QWidget *widget = qdesigner_internal::QDesignerFormBuilder::createPreview(fw, style_name);
        widget->setParent(fw->window(), Qt::Dialog);
        widget->setWindowModality(Qt::ApplicationModal);
        widget->setAttribute(Qt::WA_DeleteOnClose, true);
        widget->move(fw->mapToGlobal(QPoint(0, 0)) + QPoint(10, 10));
        widget->setWindowTitle(tr("%1 - [Preview]").arg(widget->windowTitle()));
        widget->installEventFilter(this);
        widget->show();
    }
}

void FormEditorW::addFormWindowW(FormWindowW *formWindowW)
{
    m_formWindows.append(formWindowW);
}

void FormEditorW::removeFormWindowW(FormWindowW *formWindowW)
{
    int index = m_formWindows.indexOf(formWindowW);

    if (index >= 0)
        m_formWindows.removeAt(index);
}

void FormEditorW::activeFormWindowChanged(QDesignerFormWindowInterface *afw)
{
    bool foundFormWindow = false;

    for (int i = 0; i < m_formWindows.count(); ++i)
    {
        if (FormWindowW *fw = m_formWindows[i])
        {
            QDesignerFormWindowInterface *fwd = fw->formWindow();
            QDesignerFormWindowCursorInterface *cursor = fwd->cursor();

            if (fwd == afw && cursor && cursor->isWidgetSelected(fwd->mainContainer()))
            {
                fw->updateFormWindowSelectionHandles(true);
                foundFormWindow = true;
            }
            else
            {
                fw->updateFormWindowSelectionHandles(false);
            }
        }
    }
}

FormWindowW *FormEditorW::activeFormWindowW() const
{
    if (!formEditor() || !formEditor()->formWindowManager())
        return 0;

    QDesignerFormWindowInterface *activeFormWindow = formEditor()->formWindowManager()->activeFormWindow();

    if (!activeFormWindow)
        return 0;

    Q_FOREACH (FormWindowW *formWindowW, m_formWindows)
    {
        if (formWindowW->formWindow() == activeFormWindow)
            return formWindowW;
    }
    return 0;
}

bool FormEditorW::eventFilter(QObject *watched, QEvent *event)
{
    QWidget *w = qobject_cast<QWidget *>(watched);

    if (w && w->isWindow() && event->type() == QEvent::KeyPress)
    {
        QKeyEvent *keyEvent = (QKeyEvent *)event;

        if (keyEvent && (keyEvent->key() == Qt::Key_Escape))
        {
            w->close();
            return true;
        }
    }
    return QObject::eventFilter(watched, event);
}

QAction *FormEditorW::idToAction(int id)
{
    QDesignerFormWindowManagerInterface *fwm = m_formeditor->formWindowManager();

    switch(id)
    {
		case ActionCut:
			return fwm->actionCut();

		case ActionCopy:
			return fwm->actionCopy();

		case ActionPaste:
			return fwm->actionPaste();

		case ActionDelete:
			return fwm->actionDelete();

		case ActionSelectAll:
			return fwm->actionSelectAll();

		case ActionUndo:
			return fwm->actionUndo();

		case ActionRedo:
			return fwm->actionRedo();

		case ActionLower:
			return fwm->actionLower();

		case ActionRaise:
			return fwm->actionRaise();

		case ActionHorizontalLayout:
			return fwm->actionHorizontalLayout();

		case ActionVerticalLayout:
			return fwm->actionVerticalLayout();

		case ActionSplitHorizontal:
			return fwm->actionSplitHorizontal();

		case ActionSplitVertical:
			return fwm->actionSplitVertical();

		case ActionGridLayout:
			return fwm->actionGridLayout();

		case ActionBreakLayout:
			return fwm->actionBreakLayout();

		case ActionAdjustSize:
			return fwm->actionAdjustSize();

		default:
			if (id > LastStaticAction && id <= lastAction())
			{
				return m_previewActions.at(id - LastStaticAction - 1);
			}
	}
    return 0;
}

FormEditorW::~FormEditorW()
{
    delete m_objectInspector;
    delete m_propertyEditor;
    delete m_widgetBox;
    delete m_actionEditor;
    delete m_signalSlotEditor;
    delete m_resourceEditor;
}

FormEditorW *FormEditorW::instance()
{
    if (!m_self)
        m_self = new FormEditorW();

    return m_self;
}

void FormEditorW::initializeCorePlugins()
{
    QList<QObject*> builtinPlugins = QPluginLoader::staticInstances();

    Q_FOREACH (QObject *plugin, builtinPlugins)
    {
        if (QDesignerFormEditorPluginInterface *formEditorPlugin = qobject_cast<QDesignerFormEditorPluginInterface*>(plugin))
        {
        	if (!formEditorPlugin->isInitialized())
        		formEditorPlugin->initialize(m_formeditor);
        }
    }
}

/**
 * 更新顶层控件。这个函数确保顶层控件被设置为可见控件。我们没有一个实际的顶层控件。
 */
bool FormEditorW::updateTopLevel(QWidget *delWidget)
{
    if (m_formeditor == 0)
        return false;

    if (m_formeditor->topLevel() && m_formeditor->topLevel()->isVisible() && m_formeditor->topLevel() != delWidget)
        return true;

    if (m_formeditor->widgetBox() && m_formeditor->widgetBox()->isVisible() && m_formeditor->widgetBox() != delWidget)
    {
        m_formeditor->setTopLevel(m_formeditor->widgetBox());
        return true;
    }

    /**
	if (propertyEditor().isVisible() && (propertyEditor() != exceptWidget))
        setTopLevel(propertyEditor());
    if (objectInspector().isVisible() && (objectInspector() != exceptWidget))
        setTopLevel(objectInspector());
	*/

    if (QDesignerFormWindowManagerInterface *fwm = m_formeditor->formWindowManager())
    {
        for (int i = 0; i < fwm->formWindowCount(); ++i)
        {
            if (fwm->formWindow(i)->isVisible() && fwm->formWindow(i) != delWidget)
            {
                m_formeditor->setTopLevel(fwm->formWindow(i));
                return true;
            }
        }
    }
    return false;
}

ActionChangedNotifier::ActionChangedNotifier(QObject *parent, int id) : QObject(parent)
{
    actId = id;
}

void ActionChangedNotifier::actionChanged()
{
    if (QDesignerFormWindowInterface *fw = FormEditorW::instance()->formEditor()->formWindowManager()->activeFormWindow())
    {
        if (FormWindowW *fww = qobject_cast<FormWindowW *>(fw->parentWidget()->parentWidget()->parentWidget()->parentWidget()))
            fww->signalChange(actId);
    }
}

